package com.genreate.util.pay.wx.app;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.genreate.util.pay.wx.config.WxPayConfig;
import com.genreate.util.pay.wx.domain.PayAppReq;
import com.genreate.util.pay.wx.domain.WxPayNotifyInfo;
import com.genreate.util.pay.wx.exception.WxPayExceptionHandler;
import com.genreate.util.pay.wx.util.WxPaySignatureValidator;
import com.wechat.pay.contrib.apache.httpclient.exception.ParseException;
import com.wechat.pay.contrib.apache.httpclient.exception.ValidationException;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.GsonUtil;
import com.wechat.pay.java.service.ecommercerefund.model.CreateAbnormalRefundRequest;
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.app.model.PrepayRequest;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import javax.crypto.BadPaddingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * AppService使用示例
 */
@Slf4j
public class AppServiceExample {
    public static AppService appService = getAppService();
    public static RefundService refundService = getRefundService();

    /**
     * 获取配置
     *
     * @return {@link Config }
     */
    public static Config getConfig() {
        return new RSAAutoCertificateConfig.Builder()
                .merchantId(WxPayConfig.mchid)
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKey(WxPayConfig.privateKey)
                .merchantSerialNumber(WxPayConfig.merchantSerialNumber)
                .apiV3Key(WxPayConfig.apiV3Key)
                .build();
    }

    /**
     * 获取退款服务
     *
     * @return {@link RefundService }
     */
    public static RefundService getRefundService(){
        return new RefundService.Builder().config(getConfig()).build();
    }

    /**
     * 获取应用服务
     *
     * @return {@link AppService }
     */
    public static AppService getAppService() {
        return new AppService.Builder().config(getConfig()).build();
    }

    /**
     * 退款
     *
     * @param request 请求
     * @return {@link Refund }
     */
    public static Refund refund(CreateRequest request) {
        return WxPayExceptionHandler.handle(
                () -> refundService.create(request),
                "申请退款失败"
        );
    }

    /**
     * 通过商户退款单号查询退款
     *
     * @param outRefundNo out退款编号
     * @return {@link Refund }
     */
    public static Refund queryByOutRefundNo(String outRefundNo) {
        QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
        request.setOutRefundNo(outRefundNo);
        return WxPayExceptionHandler.handle(
                () -> refundService.queryByOutRefundNo(request),
                "通过商户退款单号查询退款失败"
        );
    }

    /**
     * 支付回调
     *
     * @param serial      串行
     * @param signature   签名
     * @param timestamp   时间戳
     * @param nonce       随机数
     * @param requestBody 请求正文
     * @return {@link ResponseEntity }<{@link Object }>
     */
    public ResponseEntity<Object> handleCallbackWx(String serial, String signature, String timestamp, String nonce, String requestBody) {
        Map<String, String> errorResponse = new HashMap<>();
        // 验签和解析请求体
        try {
            NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(WxPayConfig.merchantSerialNumber)
                    .withNonce(nonce)
                    .withTimestamp(timestamp)
                    .withSignature(signature)
                    .withBody(requestBody)
                    .build();
            // 使用微信支付公钥验签器：适用于已经完成「平台证书」-->「微信支付公钥」迁移的商户以及新申请的商户
            NotificationHandler handler = new NotificationHandler(WxPayConfig.certificateVerifier, WxPayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));

            Notification notification = handler.parse(request);

            // 解析通知内容
            WxPayNotifyInfo wxPayNotifyInfo = GsonUtil.getGson().fromJson(notification.getDecryptData(), WxPayNotifyInfo.class);


        } catch (ValidationException e) {
            e.printStackTrace();
            errorResponse.put("code", "FAIL");
            errorResponse.put("message", "验签失败：" + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
        } catch (ParseException e) {
            e.printStackTrace();
            errorResponse.put("code", "FAIL");
            errorResponse.put("message", e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
        }
        errorResponse.put("code", "FAIL");
        errorResponse.put("message", "失败");
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
    }

    /**
     * 支付回调
     *
     * @param serial      串行
     * @param signature   签名
     * @param timestamp   时间戳
     * @param nonce       随机数
     * @param requestBody 请求正文
     * @return {@link ResponseEntity }<{@link Object }>
     */
//    public ResponseEntity<Object> handleCallback(String serial, String signature, String timestamp, String nonce, String requestBody) {
//        // 1. 执行验签逻辑
//        boolean verifySuccess;
//        try {
//            verifySuccess = WxPaySignatureValidator.validateSignature(
//                    serial, signature, timestamp, nonce, requestBody,
//                    "微信支付公钥内容", "平台证书内容"
//            );
//        } catch (Exception e) {
//            // 验签过程中发生异常（如证书无效、格式错误等），视为验签失败
//            verifySuccess = false;
//        }
//
//        // 2. 根据验签结果应答
//        if (verifySuccess) {
//            // 验签通过：返回200/204，无响应体
//            Notification wxPayCallback = GsonUtil.getGson().fromJson(requestBody, Notification.class);
//
//            // 获取解密后的通知内容
//            String notifyJson = WxPaySignatureValidator.decrypt(WxPayConfig.apiV3Key, nonce, wxPayCallback.getResource());
//
//            // 解析通知内容
//            WxPayNotifyInfo wxPayNotifyInfo = GsonUtil.getGson().fromJson(notifyJson, WxPayNotifyInfo.class);
//
//            //后续处理 todo
//
//            return ResponseEntity.status(HttpStatus.OK).body(null);
//        } else {
//            // 验签失败：返回4XX/5XX，带错误信息
//            Map<String, String> errorResponse = new HashMap<>();
//            errorResponse.put("code", "FAIL");
//            errorResponse.put("message", "验签失败：签名不匹配或参数无效");
//            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
//        }
//    }

    /**
     * 获取签名
     *
     * @param appId    应用程序id
     * @param prepayId 预付费id
     * @return {@link String }
     */
    public static String getSign(String appId, String prepayId) {
        return WxPaySignatureValidator.getSign(appId, prepayId, WxPayConfig.privateKey);
    }

    /**
     * 微信支付APP端调起支付请求参数实体类
     *
     * @param prepayId 预支付交易会话标识
     * @return {@link PayAppReq }
     */
    public PayAppReq getPayAppReq(String prepayId){
        PayAppReq payAppReq = new PayAppReq();
        payAppReq.setAppId(WxPayConfig.appid);
        payAppReq.setPartnerId(WxPayConfig.mchid);
        payAppReq.setPrepayId(prepayId);
        payAppReq.setNonceStr(RandomUtil.randomString(32));
        payAppReq.setTimeStamp(Convert.toStr(DateUtil.date().second()));
        payAppReq.setSign(getAppSign(prepayId));
        return payAppReq;
    }

    /**
     * 获取签名(app调起支付)
     *
     * @param prepayId 预付费id
     * @return {@link String }
     */
    public String getAppSign(String prepayId) {
        return WxPaySignatureValidator.getSign(WxPayConfig.appid, prepayId, WxPayConfig.privateKey);
    }

    /**
     * 关闭订单
     *
     * @param outTradeNo 商户订单号
     */
    public static void closeOrder(String outTradeNo) {
        CloseOrderRequest request = new CloseOrderRequest();
        // 商户号
        request.setMchid(WxPayConfig.mchid);
        request.setOutTradeNo(outTradeNo);

        WxPayExceptionHandler.handle(
                () -> {
                    appService.closeOrder(request);
                    return null;
                },
                "关闭订单失败"
        );
    }

    /**
     * APP支付下单
     *
     * @param request 请求
     * @return {@link String } 交易id
     */
    public static String prepay(PrepayRequest request) {
        request.setAppid(WxPayConfig.appid);
        request.setMchid(WxPayConfig.mchid);
        // 调用公共异常处理工具，传入下单逻辑和错误提示
        return WxPayExceptionHandler.handle(
                () -> appService.prepay(request).getPrepayId(),
                "APP支付下单失败"
        );
    }

    /**
     * 微信支付订单号查询订单
     *
     * @param transactionId 交易id
     * @return {@link Transaction }
     */
    public static Transaction queryOrderById(String transactionId) {
        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        // 设置商户号
        request.setMchid(WxPayConfig.mchid);
        request.setTransactionId(transactionId);
        // 调用公共异常处理工具
        return WxPayExceptionHandler.handle(
                () -> appService.queryOrderById(request),
                "根据微信订单号查询失败"
        );
    }

    /**
     * 商户订单号查询订单
     *
     * @param outTradeNo 商户订单号
     * @return {@link Transaction }
     */
    public static Transaction queryOrderByOutTradeNo(String outTradeNo) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        // 设置商户订单号
        request.setOutTradeNo(outTradeNo);
        // 设置商户号
        request.setMchid(WxPayConfig.mchid);
        // 调用公共异常处理工具
        return WxPayExceptionHandler.handle(
                () -> appService.queryOrderByOutTradeNo(request),
                "根据商户订单号查询失败"
        );
    }

}
